<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>移动端多选树形结构 - UniAppx</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.14/dist/vue.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'PingFang SC', 'Helvetica Neue', Arial, sans-serif;
        }

        body {
            background-color: #f5f5f5;
            color: #333;
            font-size: 14px;
            padding: 0;
            overflow: hidden;
        }

        .container {
            max-width: 480px;
            margin: 0 auto;
            background: #fff;
            height: 100vh;
            display: flex;
            flex-direction: column;
            position: relative;
        }

        /* 顶部导航 */
        .header {
            background: linear-gradient(90deg, #3498db, #2c3e50);
            color: white;
            padding: 15px;
            display: flex;
            align-items: center;
            position: sticky;
            top: 0;
            z-index: 100;
            box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        }

        .header h1 {
            font-size: 18px;
            font-weight: 600;
            flex: 1;
            text-align: center;
        }

        /* 搜索区域 */
        .search-box {
            padding: 15px;
            background: #fff;
            border-bottom: 1px solid #eee;
        }

        .search-container {
            display: flex;
            align-items: center;
            background: #f5f5f5;
            border-radius: 20px;
            padding: 8px 15px;
        }

        .search-container i {
            color: #999;
            margin-right: 8px;
        }

        .search-container input {
            flex: 1;
            border: none;
            background: transparent;
            font-size: 14px;
            outline: none;
        }

        /* 树形结构容器 */
        .tree-container {
            flex: 1;
            overflow-y: auto;
            padding: 10px 15px;
        }

        /* 树节点 */
        .tree-node {
            padding: 12px 0;
            border-bottom: 1px solid #f0f0f0;
            position: relative;
        }

        .node-content {
            display: flex;
            align-items: center;
            padding-left: 24px;
        }

        .node-toggle {
            position: absolute;
            left: 0;
            top: 14px;
            width: 24px;
            height: 24px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: #3498db;
            z-index: 2;
        }

        .node-checkbox {
            width: 22px;
            height: 22px;
            border: 1px solid #ddd;
            border-radius: 4px;
            display: flex;
            align-items: center;
            justify-content: center;
            margin-right: 10px;
            position: relative;
        }

        .node-checkbox.checked {
            background: #3498db;
            border-color: #3498db;
        }

        .node-checkbox.checked::after {
            content: '';
            width: 5px;
            height: 10px;
            border: solid white;
            border-width: 0 2px 2px 0;
            transform: rotate(45deg);
            position: absolute;
        }

        .node-checkbox.indeterminate {
            background: #3498db;
            border-color: #3498db;
        }

        .node-checkbox.indeterminate::after {
            content: '';
            width: 10px;
            height: 2px;
            background: white;
            position: absolute;
        }

        .node-label {
            flex: 1;
            font-size: 15px;
            color: #333;
        }

        .node-count {
            font-size: 13px;
            color: #999;
            margin-left: 8px;
        }

        .children-container {
            padding-left: 24px;
            overflow: hidden;
        }

        /* 底部操作栏 */
        .footer {
            display: flex;
            padding: 10px 15px;
            background: #fff;
            border-top: 1px solid #eee;
            box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
        }

        .footer-btn {
            flex: 1;
            padding: 12px;
            text-align: center;
            border-radius: 6px;
            font-weight: 500;
            font-size: 15px;
        }

        .reset-btn {
            background: #f5f5f5;
            color: #666;
            margin-right: 10px;
        }

        .confirm-btn {
            background: linear-gradient(90deg, #3498db, #2c3e50);
            color: #fff;
        }

        /* 空状态 */
        .empty-state {
            text-align: center;
            padding: 40px 20px;
            color: #999;
        }

        .empty-state i {
            font-size: 48px;
            margin-bottom: 15px;
            color: #e0e0e0;
        }

        .empty-state p {
            font-size: 14px;
            margin-top: 10px;
        }

        /* 选中标签 */
        .selected-tags {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            padding: 10px 15px;
            background: #f9f9f9;
            border-bottom: 1px solid #eee;
        }

        .selected-tag {
            background: #e1f0fa;
            color: #3498db;
            font-size: 12px;
            padding: 5px 12px;
            border-radius: 15px;
            display: flex;
            align-items: center;
        }

        .selected-tag i {
            margin-left: 4px;
            font-size: 10px;
        }

        /* 全选操作 */
        .select-all {
            display: flex;
            align-items: center;
            padding: 15px;
            background: #fff;
            border-bottom: 1px solid #eee;
            position: sticky;
            top: 60px;
            z-index: 99;
        }

        .select-all .node-checkbox {
            margin-right: 15px;
        }

        .select-all-label {
            font-size: 15px;
            font-weight: 500;
            color: #333;
        }
    </style>
</head>

<body>
    <div id="app">
        <div class="container">
            <!-- 顶部导航 -->
            <div class="header">
                <i class="fas fa-arrow-left" @click="goBack"></i>
                <h1>部门选择</h1>
                <i class="fas fa-check"></i>
            </div>

            <!-- 搜索区域 -->
            <div class="search-box">
                <div class="search-container">
                    <i class="fas fa-search"></i>
                    <input type="text" placeholder="搜索部门或人员" v-model="searchText">
                </div>
            </div>

            <!-- 全选操作 -->
            <div class="select-all">
                <div class="node-checkbox" :class="selectAllStatus" @click="toggleSelectAll"></div>
                <div class="select-all-label">全选</div>
            </div>

            <!-- 已选标签 -->
            <div class="selected-tags" v-if="selectedCount > 0">
                <div class="selected-tag" v-for="(item, index) in selectedItems" :key="index">
                    {{item.label}} <i class="fas fa-times" @click="removeItem(item)"></i>
                </div>
            </div>

            <!-- 树形结构容器 -->
            <div class="tree-container">
                <template v-if="filteredNodes.length > 0">
                    <div v-for="node in filteredNodes" :key="node.id" class="tree-node">
                        <div class="node-content">
                            <div class="node-toggle" v-if="node.children && node.children.length > 0"
                                @click="toggleExpand(node)">
                                <i :class="node.expanded ? 'fas fa-minus' : 'fas fa-plus'"></i>
                            </div>

                            <div class="node-checkbox" :class="getCheckboxClass(node)" @click="toggleCheck(node)">
                            </div>

                            <div class="node-label" @click="toggleCheck(node)">
                                {{node.label}}
                                <span class="node-count" v-if="node.children && node.children.length > 0">
                                    ({{node.children.length}})
                                </span>
                            </div>
                        </div>

                        <div class="children-container" v-if="node.expanded && node.children">
                            <div v-for="child in node.children" :key="child.id" class="tree-node">
                                <div class="node-content" style="padding-left: 24px;">
                                    <div class="node-checkbox" :class="getCheckboxClass(child)"
                                        @click="toggleCheck(child)">
                                    </div>

                                    <div class="node-label" @click="toggleCheck(child)">
                                        {{child.label}}
                                        <span class="node-count" v-if="child.children && child.children.length > 0">
                                            ({{child.children.length}})
                                        </span>
                                    </div>
                                </div>

                                <div class="children-container" v-if="child.expanded && child.children">
                                    <div v-for="subChild in child.children" :key="subChild.id" class="tree-node">
                                        <div class="node-content" style="padding-left: 48px;">
                                            <div class="node-checkbox" :class="getCheckboxClass(subChild)"
                                                @click="toggleCheck(subChild)">
                                            </div>

                                            <div class="node-label" @click="toggleCheck(subChild)">
                                                {{subChild.label}}
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </template>

                <div v-else class="empty-state">
                    <i class="fas fa-search"></i>
                    <h3>未找到匹配项</h3>
                    <p>请尝试不同的搜索词</p>
                </div>
            </div>

            <!-- 底部操作栏 -->
            <div class="footer">
                <div class="footer-btn reset-btn" @click="resetSelection">重置</div>
                <div class="footer-btn confirm-btn" @click="confirmSelection">确定 ({{selectedCount}})</div>
            </div>
        </div>
    </div>

    <script>
        new Vue({
            el: '#app',
            data: {
                searchText: '',
                treeData: [
                    {
                        id: '1',
                        label: '技术部',
                        children: [
                            {
                                id: '1-1', label: '前端开发组', children: [
                                    { id: '1-1-1', label: 'Web前端小组' },
                                    { id: '1-1-2', label: '移动端小组' }
                                ]
                            },
                            {
                                id: '1-2', label: '后端开发组', children: [
                                    { id: '1-2-1', label: 'Java开发小组' },
                                    { id: '1-2-2', label: 'Python开发小组' }
                                ]
                            },
                            { id: '1-3', label: '测试组' },
                            { id: '1-4', label: '运维组' }
                        ]
                    },
                    {
                        id: '2',
                        label: '产品部',
                        children: [
                            { id: '2-1', label: '产品设计组' },
                            { id: '2-2', label: '用户体验组' }
                        ]
                    },
                    {
                        id: '3',
                        label: '市场部',
                        children: [
                            { id: '3-1', label: '市场推广组' },
                            { id: '3-2', label: '品牌策划组' },
                            { id: '3-3', label: '数字营销组' }
                        ]
                    },
                    {
                        id: '4',
                        label: '人力资源部',
                        children: [
                            { id: '4-1', label: '招聘组' },
                            { id: '4-2', label: '培训发展组' },
                            { id: '4-3', label: '薪酬福利组' }
                        ]
                    },
                    {
                        id: '5',
                        label: '财务部',
                        children: [
                            { id: '5-1', label: '会计组' },
                            { id: '5-2', label: '财务分析组' }
                        ]
                    }
                ],
                selectedNodes: {}
            },
            computed: {
                // 处理树数据，添加状态
                processedTreeData() {
                    const addState = (nodes) => {
                        return nodes.map(node => {
                            const children = node.children ? addState(node.children) : [];
                            return {
                                ...node,
                                expanded: false,
                                checked: this.selectedNodes[node.id]?.checked || false,
                                indeterminate: this.selectedNodes[node.id]?.indeterminate || false,
                                children: children
                            };
                        });
                    };

                    return addState(this.treeData);
                },

                // 过滤后的节点
                filteredNodes() {
                    if (!this.searchText) return this.processedTreeData;

                    const searchTerm = this.searchText.toLowerCase();
                    const filterNodes = (nodes) => {
                        return nodes.filter(node => {
                            // 如果节点匹配，包括其所有子节点
                            if (node.label.toLowerCase().includes(searchTerm)) {
                                return true;
                            }

                            // 如果子节点有匹配，展开父节点
                            if (node.children && node.children.length) {
                                const matchedChildren = filterNodes(node.children);
                                if (matchedChildren.length > 0) {
                                    node.children = matchedChildren;
                                    node.expanded = true;
                                    return true;
                                }
                            }

                            return false;
                        });
                    };

                    return filterNodes([...this.processedTreeData]);
                },

                // 选中的节点
                selectedItems() {
                    const items = [];
                    const traverse = (nodes) => {
                        nodes.forEach(node => {
                            if (this.selectedNodes[node.id]?.checked) {
                                items.push({ id: node.id, label: node.label });
                            }
                            if (node.children) {
                                traverse(node.children);
                            }
                        });
                    };
                    traverse(this.treeData);
                    return items;
                },

                // 选中数量
                selectedCount() {
                    return Object.values(this.selectedNodes).filter(n => n.checked).length;
                },

                // 全选状态
                selectAllStatus() {
                    const allChecked = this.processedTreeData.every(node =>
                        this.selectedNodes[node.id]?.checked && !this.selectedNodes[node.id]?.indeterminate
                    );

                    const someChecked = this.processedTreeData.some(node =>
                        this.selectedNodes[node.id]?.checked || this.selectedNodes[node.id]?.indeterminate
                    );

                    if (allChecked) return 'checked';
                    if (someChecked) return 'indeterminate';
                    return '';
                }
            },
            methods: {
                // 切换节点展开状态
                toggleExpand(node) {
                    node.expanded = !node.expanded;
                },

                // 获取复选框状态类
                getCheckboxClass(node) {
                    if (this.selectedNodes[node.id]?.checked) return 'checked';
                    if (this.selectedNodes[node.id]?.indeterminate) return 'indeterminate';
                    return '';
                },

                // 切换节点选中状态
                toggleCheck(node) {
                    const newState = !this.selectedNodes[node.id]?.checked;

                    // 更新当前节点状态
                    this.$set(this.selectedNodes, node.id, {
                        checked: newState,
                        indeterminate: false
                    });

                    // 更新所有子节点
                    const updateChildren = (children) => {
                        if (!children) return;
                        children.forEach(child => {
                            this.$set(this.selectedNodes, child.id, {
                                checked: newState,
                                indeterminate: false
                            });
                            updateChildren(child.children);
                        });
                    };
                    updateChildren(node.children);

                    // 更新父节点状态
                    this.updateParentStatus(node);
                },

                // 更新父节点状态
                updateParentStatus(node) {
                    // 在实际应用中，这里应该向上遍历更新所有父节点的状态
                    // 由于数据结构限制，这里简化处理
                },

                // 移除已选项目
                removeItem(item) {
                    this.$set(this.selectedNodes, item.id, {
                        checked: false,
                        indeterminate: false
                    });
                },

                // 重置选择
                resetSelection() {
                    this.selectedNodes = {};
                },

                // 确认选择
                confirmSelection() {
                    console.log('已选择:', this.selectedItems);
                    alert(`已选择 ${this.selectedCount} 个项目`);
                },

                // 返回
                goBack() {
                    console.log('返回上一页');
                },

                // 切换全选
                toggleSelectAll() {
                    const currentStatus = this.selectAllStatus;
                    const newState = currentStatus !== 'checked';

                    // 更新所有节点状态
                    const updateNodes = (nodes) => {
                        nodes.forEach(node => {
                            this.$set(this.selectedNodes, node.id, {
                                checked: newState,
                                indeterminate: false
                            });
                            if (node.children) {
                                updateNodes(node.children);
                            }
                        });
                    };

                    updateNodes(this.treeData);
                }
            }
        });
    </script>
</body>

</html>